/*
 * Copyright 2002-2005 Robert Breidecker.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.sourceforge.jsorter;

import java.util.List;
import java.util.Vector;

import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

/**
 * Used for sorting data in the Swing objects JTable, JList, JComboBox,
 * DefaultTableModel, DefaultListModel, DefaultComboBoxModel and
 * SortableComponent.
 * 
 * Note: This is class is not thread safe.
 * 
 * @author Robert Breidecker
 */
public class SwingSorter {
	/**
	 * Indicate how to treat null data values.
	 */
	private int nullBehavior = SorterConstants.NULLS_ARE_INVALID;

	/**
	 * Sorts a custom component that implements the SortableComponent interface.
	 * This method assumes that the object type in the display column is made up
	 * entirely of the same type and that the type implements the Comparable
	 * interface. For example, the data in the display column is entirely made
	 * of String or Integers. Some of the objects that implement the Comparable
	 * interface include String, Integer, Long, Short, Float, Byte, Double and
	 * Date.
	 * 
	 * @param component
	 *            The component to be sorted in ascending order. The component
	 *            model must implement the SortableComponent interface.
	 */
	public void sortComponent(final SortableSwingComponent component) {
		sortComponent(component, SorterConstants.ASCENDING_ORDER);
	}

	/**
	 * Sorts a custom component that implements the SortableComponent interface.
	 * This method assumes that the object type in the display column is made up
	 * entirely of the same type and that the type implements the Comparable
	 * interface. For example, the data in the display column is entirely made
	 * of String or Integers. Some of the objects that implement the Comparable
	 * interface include String, Integer, Long, Short, Float, Byte, Double and
	 * Date.
	 * 
	 * @param component
	 *            The component to be sorted in ascending order. The component
	 *            model must implement the SortableComponent interface.
	 * 
	 * @param columnOrder
	 *            An integer that represents the type of ordering to be used
	 *            when sorting by only the first column in the data. The type of
	 *            sort can either be ascending or descending. The
	 *            ASCENDING_ORDER and DESCENDING_ORDER constants in this class
	 *            should be used.
	 */
	public void sortComponent(final SortableSwingComponent component,
			final int columnOrder) {
		final SortableColumn sortColumn = new SortableColumnImpl(0, columnOrder);
		final List sortColumns = new Vector();
		sortColumns.add(sortColumn);

		sortComponent(component, sortColumns);
	}

	/**
	 * Sorts a custom component that implements the SortableComponent interface.
	 * This method assumes that the object type in the display column is made up
	 * entirely of the same type and that the type implements the Comparable
	 * interface. For example, the data in the display column is entirely made
	 * of String or Integers. Some of the objects that implement the Comparable
	 * interface include String, Integer, Long, Short, Float, Byte, Double and
	 * Date.
	 * 
	 * @param component
	 *            The component to be sorted in ascending order. The component
	 *            model must implement the SortableComponent interface.
	 * 
	 * @param sortColumns
	 *            Sort columns are a list of numbers specifying the colums to
	 *            sort the table by. Each number in the list should be an
	 *            instance of the SortColumn class represents a position of a
	 *            column in the table. Column position start at zero just as
	 *            they do in the standard Collection classes and Java arrays.
	 *            When this field is null, there must be only one and only one
	 *            column order specified. The sort routine will then sort using
	 *            every column in the table starting from position 0 and up or
	 *            left to right in ascending order.
	 */
	public void sortComponent(final SortableSwingComponent component,
			final List sortColumns) {
		// Create a new Sorter object.
		final Sorter sorter = new Sorter(component.getDataList(), sortColumns);
		sorter.setNullBehavior(nullBehavior);

		// Sort the data!
		sorter.sort();
	}

	/**
	 * Sorts a combo box using Sorter. This method assumes that the object type
	 * in the display column is made up entirely of the same type and that the
	 * type implements the Comparable interface. For example, the data in the
	 * display column is entirely made of String or Integers. Some of the
	 * objects that implement the Comparable interface include String, Integer,
	 * Long, Short, Float, Byte, Double and Date.
	 * 
	 * @param comboBox
	 *            The combo box to be sorted in ascending order. The combo box
	 *            model must be an instance or a descendent of
	 *            DefaultComboBoxModel.
	 */
	public void sortComboBox(final JComboBox comboBox) {
		sortComboBox(comboBox, SorterConstants.ASCENDING_ORDER);
	}

	/**
	 * Sorts a combo box using Sorter. This method assumes that the object type
	 * in the display column is made up entirely of the same type and that the
	 * type implements the Comparable interface. For example, the data in the
	 * display column is entirely made of String or Integers. Some of the
	 * objects that implement the Comparable interface include String, Integer,
	 * Long, Short, Float, Byte, Double and Date.
	 * 
	 * @param comboBox
	 *            The combo box to be sorted. The combo box model must be an
	 *            instance or a descendent of DefaultComboBoxModel or implement
	 *            the SortableComponent.
	 * 
	 * @param columnOrder
	 *            An integer that represents the type of ordering to be used
	 *            when sorting by only the first column in the data. The type of
	 *            sort can either be ascending or descending. The
	 *            ASCENDING_ORDER and DESCENDING_ORDER constants in this class
	 *            should be used.
	 * 
	 * @exception IllegalArgumentException
	 *                Input data is invalid.
	 */
	public void sortComboBox(final JComboBox comboBox, final int columnOrder) {
		if (comboBox.getModel() instanceof DefaultComboBoxModel) {
			final DefaultComboBoxModel model = (DefaultComboBoxModel) comboBox
					.getModel();

			sortComboBoxModel(model, columnOrder);
		} else if (comboBox.getModel() instanceof SortableSwingComponent) {
			final SortableSwingComponent component = (SortableSwingComponent) comboBox
					.getModel();

			sortComponent(component, columnOrder);
		} else {
			throw new IllegalArgumentException(
					"ComboBox model must be an "
							+ "instance of decendent of DefaultComboBoxModel or implement the "
							+ "SortableComponent interface.");
		}
	}

	/**
	 * Sorts a combo box model using Sorter. This method assumes that the object
	 * type in the display column is made up entirely of the same type and that
	 * the type implements the Comparable interface. For example, the data in
	 * the display column is entirely made of String or Integers. Some of the
	 * objects that implement the Comparable interface include String, Integer,
	 * Long, Short, Float, Byte, Double and Date.
	 * 
	 * @param model
	 *            The combo box model to be sorted in ascending order. The combo
	 *            box model must be an instance or a descendent of
	 *            DefaultComboBoxModel.
	 */
	public void sortComboBoxModel(final DefaultComboBoxModel model) {
		sortComboBoxModel(model, SorterConstants.ASCENDING_ORDER);
	}

	/**
	 * Sorts a combo box model using Sorter. This method assumes that the object
	 * type in the display column is made up entirely of the same type and that
	 * the type implements the Comparable interface. For example, the data in
	 * the display column is entirely made of String or Integers. Some of the
	 * objects that implement the Comparable interface include String, Integer,
	 * Long, Short, Float, Byte, Double and Date.
	 * 
	 * @param model
	 *            The combo box model to be sorted. The combo box model must be
	 *            an instance or a descendent of DefaultComboBoxModel.
	 * 
	 * @param columnOrder
	 *            An integer that represents the type of ordering to be used
	 *            when sorting by only the first column in the data. The type of
	 *            sort can either be ascending or descending. The
	 *            ASCENDING_ORDER and DESCENDING_ORDER constants in this class
	 *            should be used.
	 */
	public void sortComboBoxModel(final DefaultComboBoxModel model,
			final int columnOrder) {
		// Create the table of sort data.
		final List table = new Vector();

		// Get the model data.
		for (int ctr = 0, size = model.getSize(); ctr < size; ctr++) {
			// Create a new row.
			final List row = new Vector();
			row.add(model.getElementAt(ctr));
			table.add(row);
		}

		// Create a new Sorter object.
		final Sorter sorter = new Sorter(table, columnOrder);
		sorter.setNullBehavior(nullBehavior);

		// Sort the data!
		sorter.sort();

		// Clear the model data.
		model.removeAllElements();

		// Re-add the sorted data to the model.
		for (int ctr = 0, size = table.size(); ctr < size; ctr++) {
			final List row = (List) table.get(ctr);

			// Get the first element from the row, because a list
			// only has one column.
			model.addElement(row.get(0));
		}
	}

	/**
	 * Sorts a list model using Sorter. This method assumes that the object type
	 * in the display column is made up entirely of the same type and that the
	 * type implements the Comparable interface. For example, the data in the
	 * display column is entirely made of String or Integers. Some of the
	 * objects that implement the Comparable interface include String, Integer,
	 * Long, Short, Float, Byte, Double and Date.
	 * 
	 * @param model
	 *            The list model to be sorted in ascending order. Must be a
	 *            DefaultListModel or a descendent of DefaultListModel.
	 */
	public void sortListModel(final DefaultListModel model) {
		sortListModel(model, SorterConstants.ASCENDING_ORDER);
	}

	/**
	 * Sorts a list using Sorter. This method assumes that the object type in
	 * the display column is made up entirely of the same type and that the type
	 * implements the Comparable interface. For example, the data in the display
	 * column is entirely made of String or Integers. Some of the objects that
	 * implement the Comparable interface include String, Integer, Long, Short,
	 * Float, Byte, Double and Date.
	 * 
	 * @param list
	 *            The list to be sorted in ascending order. The list model used
	 *            must be a DefaultListBoxModel or a descendent of
	 *            DefaultListModel.
	 */
	public void sortList(final JList list) {
		sortList(list, SorterConstants.ASCENDING_ORDER);
	}

	/**
	 * Sorts a list using Sorter. This method assumes that the object type in
	 * the display column is made up entirely of the same type and that the type
	 * implements the Comparable interface. For example, the data in the display
	 * column is entirely made of String or Integers. Some of the objects that
	 * implement the Comparable interface include String, Integer, Long, Short,
	 * Float, Byte, Double and Date.
	 * 
	 * @param list
	 *            The list to be sorted. The list model used must be a
	 *            DefaultListModel or a descendent of DefaultListModel or
	 *            implement the SortableComponent interface.
	 * 
	 * @param columnOrder
	 *            An integer that represents the type of ordering to be used
	 *            when sorting by only the first column in the data. The type of
	 *            sort can either be ascending or descending. The
	 *            ASCENDING_ORDER and DESCENDING_ORDER constants in this class
	 *            should be used.
	 * 
	 * @exception IllegalArgumentException
	 *                Input data is invalid.
	 */
	public void sortList(final JList list, final int columnOrder) {
		if (list.getModel() instanceof DefaultListModel) {
			final DefaultListModel model = (DefaultListModel) list.getModel();

			sortListModel(model, columnOrder);
		} else if (list.getModel() instanceof SortableSwingComponent) {
			final SortableSwingComponent model = (SortableSwingComponent) list
					.getModel();

			sortComponent(model, columnOrder);
		} else {
			throw new IllegalArgumentException(
					"List model must be an "
							+ "instance of decendent of DefaultListModel or implement the "
							+ "SortableComponent interface.");
		}
	}

	/**
	 * Sorts a list model using Sorter. This method assumes that the object type
	 * in the display column is made up entirely of the same type and that the
	 * type implements the Comparable interface. For example, the data in the
	 * display column is entirely made of String or Integers. Some of the
	 * objects that implement the Comparable interface include String, Integer,
	 * Long, Short, Float, Byte, Double and Date.
	 * 
	 * @param model
	 *            The list model to be sorted. Must be a DefaultListModel or a
	 *            descendent of DefaultListModel.
	 * 
	 * @param newSortOrder
	 *            An integer that represents the type of ordering to be used
	 *            when sorting by only the first column in the data. The type of
	 *            sort can either be ascending or descending. The
	 *            ASCENDING_ORDER and DESCENDING_ORDER constants in this class
	 *            should be used.
	 */
	public void sortListModel(final DefaultListModel model,
			final int newSortOrder) {
		// Create the table of sort data.
		final Vector table = new Vector();

		// Get the model data.
		for (int ctr = 0, size = model.getSize(); ctr < size; ctr++) {
			// Create a new row.
			final Vector row = new Vector();
			row.add(model.getElementAt(ctr));
			table.add(row);
		}

		// Create a new Sorter.
		final Sorter sorter = new Sorter(table, newSortOrder);
		sorter.setNullBehavior(nullBehavior);

		// Sort the vector data.
		sorter.sort();

		// Clear the model data.
		model.removeAllElements();

		// Re-add the sorted data to the model.
		for (int ctr = 0, size = table.size(); ctr < size; ctr++) {
			final List row = (List) table.get(ctr);

			// Get the first element from the row, because a list
			// only has one column.
			model.addElement(row.get(0));
		}
	}

	/**
	 * Sorts a table using Sorter. This method assumes that the object type in
	 * each column is made up entirely of the same type and that the type
	 * implements the Comparable interface. For example, the data in column one
	 * can be entirely made of String types and column two can be entirely made
	 * up of Integers. Some of the objects that implement the Comparable
	 * interface include String, Integer, Long, Short, Float, Byte, Double and
	 * Date. This method will sort using all columns in ascending order.
	 * 
	 * @param table
	 *            JTable The table to be sorted. The table model of the table
	 *            must be an instance or a descendent of DefaultTableModel or
	 *            must implement the SortableComponent interface.
	 */
	public void sortTable(final JTable table) {
		// Sort the table.
		sortTable(table);
	}

	/**
	 * Sorts a table using Sorter. This method assumes that the object type in
	 * each column is made up entirely of the same type and that the type
	 * implements the Comparable interface. For example, the data in column one
	 * can be entirely made of String types and column two can be entirely made
	 * up of Integers. Some of the objects that implement the Comparable
	 * interface include String, Integer, Long, Short, Float, Byte, Double and
	 * Date. This method will sort using all columns in the order specified.
	 * 
	 * @param table
	 *            JTable The table to be sorted. The table model of the table
	 *            must be an instance or a descendent of DefaultTableModel or
	 *            must implement the SortableComponent interface.
	 * 
	 * @param columnOrder
	 *            An integer that represents the type of ordering to be used
	 *            when sorting by only the first column in the data. The type of
	 *            sort can either be ascending or descending. The
	 *            ASCENDING_ORDER and DESCENDING_ORDER constants in this class
	 *            should be used.
	 */
	public void sortTable(final JTable table, final int columnOrder) {
		// Sort the table!
		sortTable(table, columnOrder);
	}

	/**
	 * Sorts a table using Sorter. This method assumes that the object type in
	 * each column is made up entirely of the same type and that the type
	 * implements the Comparable interface. For example, the data in column one
	 * can be entirely made of String types and column two is can be entriely
	 * made up of Integers. Some of the objects that implement the Comparable
	 * interface include String, Integer, Long, Short, Float, Byte, Double and
	 * Date.
	 * 
	 * @param table
	 *            JTable The table to be sorted. The table model of the table
	 *            must be an instance or a descendent of DefaultTableModel or
	 *            must implement the SortableComponent interface.
	 * 
	 * @param sortColumns
	 *            Sort columns are a list of numbers specifying the colums to
	 *            sort the table by. Each number in the list should be an
	 *            instance of the SortColumn class represents a position of a
	 *            column in the table. Column position start at zero just as
	 *            they do in the standard Collection classes and Java arrays.
	 *            When this field is null, there must be only one and only one
	 *            column order specified. The sort routine will then sort using
	 *            every column in the table starting from position 0 and up or
	 *            left to right in ascending order.
	 * 
	 * @exception IllegalArgumentException
	 *                Input data is invalid.
	 */
	public void sortTable(final JTable table, final List sortColumns) {
		if (table.getModel() instanceof DefaultTableModel) {
			final DefaultTableModel model = (DefaultTableModel) table
					.getModel();

			sortTableModel(model, sortColumns);
		} else if (table.getModel() instanceof SortableSwingComponent) {
			final SortableSwingComponent model = (SortableSwingComponent) table
					.getModel();

			sortComponent(model, sortColumns);
		} else {
			throw new IllegalArgumentException(
					"Table model must be an "
							+ "instance of decendent of DefaultTableModel or implement the "
							+ "SortableComponent interface.");
		}
	}

	/**
	 * Sorts a table model using Sorter. This method assumes that the object
	 * type in each column is made up entirely of the same type and that the
	 * type implements the Comparable interface. For example, the data in column
	 * one can be entirely made of String types and column two is can be
	 * entriely made up of Integers. Some of the objects that implement the
	 * Comparable interface include String, Integer, Long, Short, Float, Byte,
	 * Double and Date.
	 * 
	 * @param model
	 *            The table model to be sorted. The table model must be an
	 *            instance or a descendent of DefaultTableModel.
	 */
	public void sortTableModel(final DefaultTableModel model) {
		// Sort the data.
		sortTableModel(model);
	}

	/**
	 * Sorts a table model using Sorter. This method assumes that the object
	 * type in each column is made up entirely of the same type and that the
	 * type implements the Comparable interface. For example, the data in column
	 * one can be entirely made of String types and column two is can be
	 * entriely made up of Integers. Some of the objects that implement the
	 * Comparable interface include String, Integer, Long, Short, Float, Byte,
	 * Double and Date.
	 * 
	 * @param model
	 *            The table model to be sorted. The table model must be an
	 *            instance or a descendent of DefaultTableModel.
	 * 
	 * @param columnOrder
	 *            An integer that represents the type of ordering to be used
	 *            when sorting by only the first column in the data. The type of
	 *            sort can either be ascending or descending. The
	 *            ASCENDING_ORDER and DESCENDING_ORDER constants in this class
	 *            should be used.
	 */
	public void sortTableModel(final DefaultTableModel model,
			final int columnOrder) {
		// Sort the table.
		sortTableModel(model, columnOrder);
	}

	/**
	 * Sorts a table model using Sorter. This method assumes that the object
	 * type in each column is made up entirely of the same type and that the
	 * type implements the Comparable interface. For example, the data in column
	 * one can be entirely made of String types and column two is can be
	 * entriely made up of Integers. Some of the objects that implement the
	 * Comparable interface include String, Integer, Long, Short, Float, Byte,
	 * Double and Date.
	 * 
	 * @param model
	 *            The table model to be sorted. The table model must be an
	 *            instance or a descendent of DefaultTableModel.
	 * 
	 * @param sortColumns
	 *            Sort columns are a list of numbers specifying the colums to
	 *            sort the table by. Each number in the list should be an
	 *            instance of the SortColumn class represents a position of a
	 *            column in the table. Column position start at zero just as
	 *            they do in the standard Collection classes and Java arrays.
	 *            When this field is null, there must be only one and only one
	 *            column order specified. The sort routine will then sort using
	 *            every column in the table starting from position 0 and up or
	 *            left to right in ascending order.
	 */
	public void sortTableModel(final DefaultTableModel model,
			final List sortColumns) {
		// Create a new Sorter.
		final Sorter sorter = new Sorter(model.getDataVector(), sortColumns);
		sorter.setNullBehavior(nullBehavior);

		// Sort the data.
		sorter.sort();
	}

	/**
	 * Returns the null behavior for this object.
	 * 
	 * @return An integer representing the constant that indicates how null data
	 *         values should behave while being sorted. See the null behavior
	 *         constants in this class. The default value for this class is
	 *         NULLS_ARE_INVALID.
	 */
	public int getNullBehavior() {
		return nullBehavior;
	}

	/**
	 * Set the null behavior for this object.
	 * 
	 * @param nullBehavior
	 *            An integer representing the constant that indicates how null
	 *            data values should behave while being sorted. See the null
	 *            behavior constants in this class.
	 * 
	 * @throws IllegalArgumentException
	 *             Thrown if the null behavior value is not valid.
	 */
	public void setNullBehavior(final int nullBehavior) {
		if (nullBehavior != SorterConstants.NULLS_ARE_GREATEST
				&& nullBehavior != SorterConstants.NULLS_ARE_INVALID
				&& nullBehavior != SorterConstants.NULLS_ARE_LEAST) {
			throw new IllegalArgumentException("Invalid null behavior.");
		}

		this.nullBehavior = nullBehavior;
	}
}